package vector

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/qdrant/go-client/qdrant"
	"go-file-perception-model/internal/logger"
	"go-file-perception-model/internal/model"
	"go.uber.org/zap"
)

// QdrantPointsManager Qdrant点管理器
type QdrantPointsManager struct {
	client *QdrantClient
}

// NewQdrantPointsManager 创建点管理器
func NewQdrantPointsManager(client *QdrantClient) *QdrantPointsManager {
	return &QdrantPointsManager{
		client: client,
	}
}

// InsertPoints 插入向量点
func (m *QdrantPointsManager) InsertPoints(ctx context.Context, points []*model.FileChunk) error {
	if len(points) == 0 {
		return nil
	}

	// 转换为Qdrant点格式
	qdrantPoints := make([]*qdrant.PointStruct, 0, len(points))
	for _, chunk := range points {
		// 确保所有字符串都是有效的UTF-8编码
		filePath := ensureUTF8String(chunk.FilePath)
		content := ensureUTF8String(chunk.Content)
		
		// 创建点结构
		point := &qdrant.PointStruct{
			Id:      qdrant.NewID(chunk.ID),
			Vectors: qdrant.NewVectors(chunk.Vector...),
			Payload: qdrant.NewValueMap(map[string]interface{}{
				"file_id":    chunk.FileID,
				"file_path":  filePath,
				"content":    content,
				"chunk_no":   chunk.ChunkNo,
				"start_pos":  chunk.StartPos,
				"end_pos":    chunk.EndPos,
				"start_line": chunk.StartLine,
				"end_line":   chunk.EndLine,
				"created_at": chunk.CreatedAt.Format(time.RFC3339),
				"updated_at": chunk.UpdatedAt.Format(time.RFC3339),
			}),
		}
		qdrantPoints = append(qdrantPoints, point)
	}

	// 插入点
	upsertPoints := qdrant.UpsertPoints{
		CollectionName: m.client.GetCollection(),
		Points:         qdrantPoints,
	}

	_, err := m.client.GetClient().Upsert(ctx, &upsertPoints)
	if err != nil {
		return fmt.Errorf("failed to upsert points: %w", err)
	}

	logger.Info("Points inserted successfully", 
		zap.Int("count", len(points)),
		zap.String("collection", m.client.GetCollection()))
	return nil
}

// ensureUTF8String 确保字符串是有效的UTF-8编码
func ensureUTF8String(s string) string {
	// 如果已经是有效的UTF-8编码，直接返回
	if utf8.ValidString(s) {
		return s
	}

	// 如果不是有效的UTF-8编码，尝试修复
	// 这里使用简单的方法：替换无效的UTF-8序列为Unicode替换字符
	var result []rune
	for i, r := range s {
		if r == utf8.RuneError {
			// 检查是否真的是无效的UTF-8序列
			_, size := utf8.DecodeRuneInString(s[i:])
			if size == 1 {
				// 无效的UTF-8序列，添加替换字符
				result = append(result, utf8.RuneError)
			} else {
				// 误报，保留原字符
				result = append(result, r)
			}
		} else {
			result = append(result, r)
		}
	}
	return string(result)
}

// DeletePoints 删除向量点
func (m *QdrantPointsManager) DeletePoints(ctx context.Context, ids []string) error {
	if len(ids) == 0 {
		return nil
	}

	// 转换ID格式
	pointIds := make([]*qdrant.PointId, 0, len(ids))
	for _, id := range ids {
		pointIds = append(pointIds, qdrant.NewID(id))
	}

	// 创建点选择器
	pointsSelector := qdrant.NewPointsSelectorIDs(pointIds)

	// 删除点
	deletePoints := qdrant.DeletePoints{
		CollectionName: m.client.GetCollection(),
		Points:         pointsSelector,
	}

	_, err := m.client.GetClient().Delete(ctx, &deletePoints)
	if err != nil {
		return fmt.Errorf("failed to delete points: %w", err)
	}

	logger.Info("Points deleted successfully", 
		zap.Int("count", len(ids)),
		zap.String("collection", m.client.GetCollection()))
	return nil
}

// DeletePointsByFilter 根据过滤器删除向量点
func (m *QdrantPointsManager) DeletePointsByFilter(ctx context.Context, filter *Filter) error {
	// 将Filter转换为qdrant.Filter
	qdrantFilter := convertToQdrantFilter(filter)
	
	// 创建点选择器
	pointsSelector := qdrant.NewPointsSelectorFilter(qdrantFilter)

	// 删除点
	deletePoints := qdrant.DeletePoints{
		CollectionName: m.client.GetCollection(),
		Points:         pointsSelector,
	}

	_, err := m.client.GetClient().Delete(ctx, &deletePoints)
	if err != nil {
		return fmt.Errorf("failed to delete points by filter: %w", err)
	}

	logger.Info("Points deleted by filter successfully",
		zap.String("collection", m.client.GetCollection()))
	return nil
}

// DeleteAllPoints 删除所有向量点
func (m *QdrantPointsManager) DeleteAllPoints(ctx context.Context) error {
	// 创建空过滤器，匹配所有点
	filter := &qdrant.Filter{}
	
	// 创建点选择器
	pointsSelector := qdrant.NewPointsSelectorFilter(filter)

	// 删除所有点
	deletePoints := qdrant.DeletePoints{
		CollectionName: m.client.GetCollection(),
		Points:         pointsSelector,
	}

	_, err := m.client.GetClient().Delete(ctx, &deletePoints)
	if err != nil {
		return fmt.Errorf("failed to delete all points: %w", err)
	}

	logger.Info("All points deleted successfully",
		zap.String("collection", m.client.GetCollection()))
	return nil
}

// CountPoints 计算集合中的点数量
func (m *QdrantPointsManager) CountPoints(ctx context.Context, filter *Filter) (uint64, error) {
	countRequest := qdrant.CountPoints{
		CollectionName: m.client.GetCollection(),
	}

	if filter != nil {
		// 将Filter转换为qdrant.Filter
		qdrantFilter := convertToQdrantFilter(filter)
		countRequest.Filter = qdrantFilter
	}

	countResponse, err := m.client.GetClient().Count(ctx, &countRequest)
	if err != nil {
		return 0, fmt.Errorf("failed to count points: %w", err)
	}

	return countResponse, nil
}

// GetPointByID 根据点ID获取点的详细信息
func (m *QdrantPointsManager) GetPointByID(ctx context.Context, id string) (*model.SearchResult, error) {
	// 创建点ID
	pointID := qdrant.NewID(id)
	
	// 创建获取点请求
	getPoints := &qdrant.GetPoints{
		CollectionName: m.client.GetCollection(),
		Ids:            []*qdrant.PointId{pointID},
		WithPayload:    qdrant.NewWithPayloadEnable(true),
		WithVectors:    qdrant.NewWithVectorsEnable(true),
	}
	
	// 执行获取点请求
	response, err := m.client.GetClient().Get(ctx, getPoints)
	if err != nil {
		return nil, fmt.Errorf("failed to get point by id: %w", err)
	}
	
	if len(response) == 0 {
		return nil, fmt.Errorf("point not found")
	}
	
	// 获取第一个点
	point := response[0]
	payload := point.Payload
	filePath := getStringFromPayload(payload, "file_path")
	fileName := filepath.Base(filePath)
	
	// 根据文件扩展名确定文件类型
	fileType := determineFileType(filePath)
	
	// 构建结果
	result := &model.SearchResult{
		ChunkID:   point.Id.String(),
		Content:   getStringFromPayload(payload, "content"),
		Score:     0, // 单点查询没有相似度分数
		ChunkNo:   getIntFromPayload(payload, "chunk_no"),
		StartPos:  getIntFromPayload(payload, "start_pos"),
		EndPos:    getIntFromPayload(payload, "end_pos"),
		StartLine: getIntFromPayload(payload, "start_line"),
		EndLine:   getIntFromPayload(payload, "end_line"),
		FileID:    getStringFromPayload(payload, "file_id"),
		FilePath:  filePath,
		FileName:  fileName,
		FileType:  fileType,
	}
	
	return result, nil
}

// determineFileType 根据文件路径确定文件类型
func determineFileType(filePath string) string {
	if filePath == "" {
		return "unknown"
	}
	
	ext := strings.ToLower(filepath.Ext(filePath))
	switch ext {
	case ".txt":
		return "text"
	case ".md":
		return "markdown"
	case ".go":
		return "go"
	case ".py":
		return "python"
	case ".js":
		return "javascript"
	case ".ts":
		return "typescript"
	case ".java":
		return "java"
	case ".c":
		return "c"
	case ".cpp", ".cxx", ".cc", ".h", ".hpp":
		return "c++"
	default:
		return "unknown"
	}
}

// getStringFromPayload 从载荷中获取字符串值
func getStringFromPayload(payload map[string]*qdrant.Value, key string) string {
	if val, ok := payload[key]; ok {
		return val.GetStringValue()
	}
	return ""
}

// getIntFromPayload 从载荷中获取整数值
func getIntFromPayload(payload map[string]*qdrant.Value, key string) int {
	if val, ok := payload[key]; ok {
		return int(val.GetIntegerValue())
	}
	return 0
}